<?php
class Xml {
	private $_sHtml;
	private $_oElement;
	private $_sTmpTag;
	
	public function file2dom($sFile) {
		$this->_sHtml = @file_get_contents ( $sFile );
		if (! $this->_sHtml) {
			return false;
		} else {
			return $this->_parse ();
		}
	}
	
	public function html2dom($sHtml) {
		$this->_sHtml = $sHtml;
		return $this->_parse ();
	}
	
	private function _parse() {
		$this->_remove ();
		$oDom = new DOMDocument ( '1.0', 'utf-8' );
		$oDom->formatOutput = true;
		$this->_parseHtml ( $this->_sHtml, $oDom );
		return $oDom;
	}
	
	private function _remove() {
		$aSearch [] = "'<!--(.*?)-->'is";
		$aSearch [] = "'<!(.*?)>'is";
		$aSearch [] = "'<script[^>]*?>.*?</script>'is";
		$aSearch [] = "'<style[^>]*?>.*?</style>'is";
		$aSearch [] = "'[\r\n]+'";
		$aSearch [] = "'>\s+<'";
		$aSearch [] = "'(<(\w+?)\s+[^>]+?)\/>'";
		$aReplace [] = '';
		$aReplace [] = '';
		$aReplace [] = '';
		$aReplace [] = '';
		$aReplace [] = '';
		$aReplace [] = '><';
		$aReplace [] = '\\1></\\2>';
		$this->_sHtml = preg_replace ( $aSearch, $aReplace, $this->_sHtml );
	}
	
	private function _parseHtml($sHtml, $oDom) {
		$sHtml = trim ( $sHtml );
		$aTag = array ();
		preg_match ( '/^<(.+?)(\s+[^>]*)*>/', $sHtml, $aTag );
		if (count ( $aTag )) {
			$sTag = $aTag [1];
			$this->_oElement = $oDom->appendChild ( new DOMElement ( $sTag ) );
			$sHtml = $this->_getHtml ( $sHtml, $sTag );
			if ($sHtml != '') {
				$this->_parseHtml ( $sHtml, $oDom );
			}
		} elseif (preg_match ( '/^[^<]+$/', $sHtml )) {
			$oDom->appendChild ( new DOMText ( $sHtml ) );
		} elseif ($sHtml != '') {
			$aMatch = array ();
			preg_match ( '/^(.*?)(<.*)$/', $sHtml, $aMatch );
			$oDom->appendChild ( new DOMText ( $aMatch [1] ) );
			$this->_parseHtml ( $aMatch [2], $oDom );
		}
	}
	
	private function _getHtml($sHtml, $sTag) {
		$this->_sTmpTag = $sTag;
		$aMatch = array ();
		preg_match_all ( '/<' . $sTag . '.*?>/', $sHtml, $aMatch );
		$sHtml = preg_replace ( '/(.+?<)' . $sTag . '(.*?>.*?<\/)' . $sTag . '(>)/', '\\1TMPTAG\\2TMPTAG\\3', $sHtml );
		preg_match_all ( '/<' . $sTag . '.*?>/', $sHtml, $aMatch );
		if (count ( $aMatch [0] ) > 1) {
			$sHtml = $this->_getHtml ( $sHtml, $sTag );
		} else {
			$sHtml = preg_replace_callback ( '/^<' . $sTag . '(\s+([^>]*))*>((.*?)<\/' . $sTag . '>)?/', array (&$this, '_removeTag' ), $sHtml );
			$sHtml = preg_replace ( '/TMPTAG/', $sTag, $sHtml );
		}
		return $sHtml;
	}
	
	private function _removeTag($aMatch) {
		$this->_setAttr ( $aMatch [2], $this->_oElement );
		if ($aMatch [4]) {
			$sHtml = preg_replace ( '/TMPTAG/', $this->_sTmpTag, $aMatch [4] );
			$this->_parseHtml ( $sHtml, $this->_oElement );
		}
		return '';
	}
	
	private function _setAttr($sAttrs, $oElement) {
		$aMatch = array ();
		preg_match_all ( '/([\w\-]+)(=(\"?)([^\\3]+?)(\\3\s))*/', $sAttrs . ' ', $aMatch );
		if (count ( $aMatch [0] )) {
			foreach ( $aMatch [1] as $iKey => $sRow ) {
				if ($aMatch [4] [$iKey] == '') {
					$sValue = true;
				} else {
					$sValue = $aMatch [4] [$iKey];
				}
				$oElement->setAttribute ( $sRow, $sValue );
			}
		}
	}
	
	public function array2xml($aData, $sFile = null) {
		if (is_array ( $aData )) {
			$oDom = new DOMDocument ( '1.0', 'utf-8' );
			$oDom->formatOutput = true;
			$oDom = $this->_array2xml ( $oDom, $aData );
			if ($oDom) {
				if ($sFile != null) {
					if ($oDom->save ( $sFile )) {
						return true;
					} else {
						return false;
					}
				} else {
					return $oDom;
				}
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
	
	private function _array2xml($oDom, $aData, $sElementName = null) {
		foreach ( $aData as $sKey => $aRow ) {
			if (is_string ( $sKey )) {
				$sName = $sKey;
			} elseif (! $sElementName) {
				return false;
			} else {
				$sName = $sElementName;
			}
			if (is_array ( $aRow )) {
				if (! is_int ( key ( $aRow ) )) {
					$oElement = $oDom->appendChild ( new DOMElement ( htmlspecialchars ( $sName ) ) );
				} else {
					$oElement = $oDom;
				}
				$this->_array2xml ( $oElement, $aRow, $sName );
			} elseif ($sName == 'text') {
				$oDom->appendChild ( new DOMText ( htmlspecialchars ( $aRow ) ) );
			} else {
				$oDom->setAttribute ( htmlspecialchars ( $sName ), htmlspecialchars ( $aRow ) );
			}
		}
		return $oDom;
	}
	
	public function dom2array($oDom, $bIsRoot = false) {
		if ($oDom->nodeType == 9) {
			$aChild = $oDom->childNodes;
			foreach ( $aChild as $oChild ) {
				$aData = $this->dom2array ( $oChild, true );
			}
		} elseif ($oDom->nodeType == 1) {
			$sName = $oDom->nodeName;
			if ($oDom->hasAttributes ()) {
				foreach ( $oDom->attributes as $oAttr ) {
					$aData [$sName] [$oAttr->nodeName] = $oAttr->nodeValue;
				}
			}
			if ($oDom->hasChildNodes ()) {
				$aChild = $oDom->childNodes;
				foreach ( $aChild as $oChild ) {
					if ($oChild->nodeType == 3) {
						$aData [$sName] ['text'] = $oChild->nodeValue;
					} else {
						$aData [$sName] [$oChild->nodeName] [] = $this->dom2array ( $oChild );
					}
				}
			}
			if (! $bIsRoot) {
				$aData = $aData [$sName];
			}
		}
		return $aData;
	}
}